/*
* Model.java
* Copyright (C) 2003
*
* $Id: Model.java,v 1.7 2005/05/07 23:45:19 cawe Exp $
*/
/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
using System;
using Defines = Suake2.UI.Defines;
using VID = Suake2.UI.client.VID;
using cplane_t = Suake2.UI.game.cplane_t;
using cvar_t = Suake2.UI.game.cvar_t;
using Suake2.UI.qcommon;
using Suake2.UI.render;
using Math3D = Suake2.UI.util.Math3D;
using Vargs = Suake2.UI.util.Vargs;
//UPGRADE_TODO: The type 'java.nio.ByteBuffer' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using ByteBuffer = java.nio.ByteBuffer;
//UPGRADE_TODO: The type 'java.nio.ByteOrder' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using ByteOrder = java.nio.ByteOrder;
namespace Suake2.UI.render.jogl
{
	
	/// <summary> Model
	/// 
	/// </summary>
	/// <author>  cwei
	/// </author>
	public abstract class Model:Surf
	{
		public Model()
		{
			InitBlock();
		}
		private void  InitBlock()
		{
			mod_novis = new sbyte[Defines.MAX_MAP_LEAFS / 8];
			decompressed = new sbyte[Defines.MAX_MAP_LEAFS / 8];
			model_visibility = new sbyte[Defines.MAX_MAP_VISIBILITY];
		}
		
		// models.c -- model loading and caching
		
		internal model_t loadmodel;
		internal int modfilelen;
		
		//UPGRADE_NOTE: The initialization of  'mod_novis' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal sbyte[] mod_novis;
		
		internal const int MAX_MOD_KNOWN = 512;
		internal model_t[] mod_known = new model_t[MAX_MOD_KNOWN];
		internal int mod_numknown;
		
		// the inline * models from the current map are kept seperate
		internal model_t[] mod_inline = new model_t[MAX_MOD_KNOWN];
		
		internal abstract void  GL_SubdivideSurface(msurface_t surface); // Warp.java
		
		/*
		===============
		Mod_PointInLeaf
		===============
		*/
		internal override mleaf_t Mod_PointInLeaf(float[] p, model_t model)
		{
			mnode_t node;
			float d;
			cplane_t plane;
			
			if (model == null || model.nodes == null)
				Com.Error(Defines.ERR_DROP, "Mod_PointInLeaf: bad model");
			
			node = model.nodes[0]; // root node
			while (true)
			{
				if (node.contents != - 1)
					return (mleaf_t) node;
				
				plane = node.plane;
				d = Math3D.DotProduct(p, plane.normal) - plane.dist;
				if (d > 0)
					node = node.children[0];
				else
					node = node.children[1];
			}
			// never reached
		}
		
		
		//UPGRADE_NOTE: The initialization of  'decompressed' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal sbyte[] decompressed;
		//UPGRADE_NOTE: The initialization of  'model_visibility' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal sbyte[] model_visibility;
		
		/*
		===================
		Mod_DecompressVis
		===================
		*/
		internal virtual sbyte[] Mod_DecompressVis(sbyte[] in_Renamed, int offset, model_t model)
		{
			int c;
			sbyte[] out_Renamed;
			int outp, inp;
			int row;
			
			row = (model.vis.numclusters + 7) >> 3;
			out_Renamed = decompressed;
			outp = 0;
			inp = offset;
			
			if (in_Renamed == null)
			{
				// no vis info, so make all visible
				while (row != 0)
				{
					out_Renamed[outp++] = (sbyte) SupportClass.Identity(0xFF);
					row--;
				}
				return decompressed;
			}
			
			do 
			{
				if (in_Renamed[inp] != 0)
				{
					out_Renamed[outp++] = in_Renamed[inp++];
					continue;
				}
				
				c = in_Renamed[inp + 1] & 0xFF;
				inp += 2;
				while (c != 0)
				{
					out_Renamed[outp++] = 0;
					c--;
				}
			}
			while (outp < row);
			
			return decompressed;
		}
		
		/*
		==============
		Mod_ClusterPVS
		==============
		*/
		internal override sbyte[] Mod_ClusterPVS(int cluster, model_t model)
		{
			if (cluster == - 1 || model.vis == null)
				return mod_novis;
			//return Mod_DecompressVis( (byte *)model.vis + model.vis.bitofs[cluster][Defines.DVIS_PVS], model);
			return Mod_DecompressVis(model_visibility, model.vis.bitofs[cluster][Defines.DVIS_PVS], model);
		}
		
		
		//	  ===============================================================================
		
		/*
		================
		Mod_Modellist_f
		================
		*/
		internal override void  Mod_Modellist_f()
		{
			int i;
			model_t mod;
			int total;
			
			total = 0;
			VID.Printf(Defines.PRINT_ALL, "Loaded models:\n");
			for (i = 0; i < mod_numknown; i++)
			{
				mod = mod_known[i];
				if (mod.name.Length == 0)
					continue;
				
				VID.Printf(Defines.PRINT_ALL, "%8i : %s\n", new Vargs(2).add(mod.extradatasize).add(mod.name));
				total += mod.extradatasize;
			}
			VID.Printf(Defines.PRINT_ALL, "Total resident: " + total + '\n');
		}
		
		/*
		===============
		Mod_Init
		===============
		*/
		internal override void  Mod_Init()
		{
			// init mod_known
			for (int i = 0; i < MAX_MOD_KNOWN; i++)
			{
				mod_known[i] = new model_t();
			}
			SupportClass.ArraySupport.Fill(SupportClass.ToByteArray(mod_novis), (byte) SupportClass.Identity(0xff));
		}
		
		internal sbyte[] fileBuffer;
		
		/*
		==================
		Mod_ForName
		
		Loads in a model for the given name
		==================
		*/
		internal virtual model_t Mod_ForName(System.String name, bool crash)
		{
			model_t mod = null;
			int i;
			
			if (name == null || name.Length == 0)
				Com.Error(Defines.ERR_DROP, "Mod_ForName: NULL name");
			
			//
			// inline models are grabbed only from worldmodel
			//
			if (name[0] == '*')
			{
				i = System.Int32.Parse(name.Substring(1));
				if (i < 1 || r_worldmodel == null || i >= r_worldmodel.numsubmodels)
					Com.Error(Defines.ERR_DROP, "bad inline model number");
				return mod_inline[i];
			}
			
			//
			// search the currently loaded models
			//
			for (i = 0; i < mod_numknown; i++)
			{
				mod = mod_known[i];
				
				if (mod.name.Length == 0)
					continue;
				if (mod.name.Equals(name))
					return mod;
			}
			
			//
			// find a free model slot spot
			//
			for (i = 0; i < mod_numknown; i++)
			{
				mod = mod_known[i];
				
				if (mod.name.Length == 0)
					break; // free spot
			}
			if (i == mod_numknown)
			{
				if (mod_numknown == MAX_MOD_KNOWN)
					Com.Error(Defines.ERR_DROP, "mod_numknown == MAX_MOD_KNOWN");
				mod_numknown++;
				mod = mod_known[i];
			}
			
			mod.name = name;
			
			//
			// load the file
			//
			fileBuffer = FS.LoadFile(name);
			
			if (fileBuffer == null)
			{
				if (crash)
					Com.Error(Defines.ERR_DROP, "Mod_NumForName: " + mod.name + " not found");
				
				mod.name = "";
				return null;
			}
			
			modfilelen = fileBuffer.Length;
			
			loadmodel = mod;
			
			//
			// fill it in
			//
			ByteBuffer bb = ByteBuffer.wrap(fileBuffer);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			// call the apropriate loader
			
			bb.mark();
			int ident = bb.getInt();
			
			bb.reset();
			
			switch (ident)
			{
				
				case qfiles.IDALIASHEADER: 
					Mod_LoadAliasModel(mod, bb);
					break;
				
				case qfiles.IDSPRITEHEADER: 
					Mod_LoadSpriteModel(mod, bb);
					break;
				
				case qfiles.IDBSPHEADER: 
					Mod_LoadBrushModel(mod, bb);
					break;
				
				default: 
					Com.Error(Defines.ERR_DROP, "Mod_NumForName: unknown fileid for " + mod.name);
					break;
				
			}
			
			this.fileBuffer = null; // free it for garbage collection
			return mod;
		}
		
		/*
		===============================================================================
		
		BRUSHMODEL LOADING
		
		===============================================================================
		*/
		
		internal sbyte[] mod_base;
		
		
		/*
		=================
		Mod_LoadLighting
		=================
		*/
		internal virtual void  Mod_LoadLighting(lump_t l)
		{
			if (l.filelen == 0)
			{
				loadmodel.lightdata = null;
				return ;
			}
			// memcpy (loadmodel.lightdata, mod_base + l.fileofs, l.filelen);
			loadmodel.lightdata = new sbyte[l.filelen];
			Array.Copy(mod_base, l.fileofs, loadmodel.lightdata, 0, l.filelen);
		}
		
		
		/*
		=================
		Mod_LoadVisibility
		=================
		*/
		internal virtual void  Mod_LoadVisibility(lump_t l)
		{
			int i;
			
			if (l.filelen == 0)
			{
				loadmodel.vis = null;
				return ;
			}
			
			Array.Copy(mod_base, l.fileofs, model_visibility, 0, l.filelen);
			
			ByteBuffer bb = ByteBuffer.wrap(model_visibility, 0, l.filelen);
			
			loadmodel.vis = new qfiles.dvis_t(bb.order(ByteOrder.LITTLE_ENDIAN));
			
			/* done:
			memcpy (loadmodel.vis, mod_base + l.fileofs, l.filelen);
			
			loadmodel.vis.numclusters = LittleLong (loadmodel.vis.numclusters);
			for (i=0 ; i<loadmodel.vis.numclusters ; i++)
			{
			loadmodel.vis.bitofs[i][0] = LittleLong (loadmodel.vis.bitofs[i][0]);
			loadmodel.vis.bitofs[i][1] = LittleLong (loadmodel.vis.bitofs[i][1]);
			}
			*/
		}
		
		
		/*
		=================
		Mod_LoadVertexes
		=================
		*/
		internal virtual void  Mod_LoadVertexes(lump_t l)
		{
			mvertex_t[] vertexes;
			int i, count;
			
			if ((l.filelen % mvertex_t.DISK_SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
			
			count = l.filelen / mvertex_t.DISK_SIZE;
			
			vertexes = new mvertex_t[count];
			
			loadmodel.vertexes = vertexes;
			loadmodel.numvertexes = count;
			
			ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			for (i = 0; i < count; i++)
			{
				vertexes[i] = new mvertex_t(bb);
			}
		}
		
		/*
		=================
		RadiusFromBounds
		=================
		*/
		internal virtual float RadiusFromBounds(float[] mins, float[] maxs)
		{
			float[] corner = new float[]{0, 0, 0};
			
			for (int i = 0; i < 3; i++)
			{
				corner[i] = System.Math.Abs(mins[i]) > System.Math.Abs(maxs[i])?System.Math.Abs(mins[i]):System.Math.Abs(maxs[i]);
			}
			return Math3D.VectorLength(corner);
		}
		
		
		/*
		=================
		Mod_LoadSubmodels
		=================
		*/
		internal virtual void  Mod_LoadSubmodels(lump_t l)
		{
			qfiles.dmodel_t in_Renamed;
			mmodel_t[] out_Renamed;
			int i, j, count;
			
			if ((l.filelen % qfiles.dmodel_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
			
			count = l.filelen / qfiles.dmodel_t.SIZE;
			// out = Hunk_Alloc ( count*sizeof(*out));
			out_Renamed = new mmodel_t[count];
			
			loadmodel.submodels = out_Renamed;
			loadmodel.numsubmodels = count;
			
			ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			for (i = 0; i < count; i++)
			{
				in_Renamed = new qfiles.dmodel_t(bb);
				out_Renamed[i] = new mmodel_t();
				for (j = 0; j < 3; j++)
				{
					// spread the mins / maxs by a pixel
					out_Renamed[i].mins[j] = in_Renamed.mins[j] - 1;
					out_Renamed[i].maxs[j] = in_Renamed.maxs[j] + 1;
					out_Renamed[i].origin[j] = in_Renamed.origin[j];
				}
				out_Renamed[i].radius = RadiusFromBounds(out_Renamed[i].mins, out_Renamed[i].maxs);
				out_Renamed[i].headnode = in_Renamed.headnode;
				out_Renamed[i].firstface = in_Renamed.firstface;
				out_Renamed[i].numfaces = in_Renamed.numfaces;
			}
		}
		
		/*
		=================
		Mod_LoadEdges
		=================
		*/
		internal virtual void  Mod_LoadEdges(lump_t l)
		{
			medge_t[] edges;
			int i, count;
			
			if ((l.filelen % medge_t.DISK_SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
			
			count = l.filelen / medge_t.DISK_SIZE;
			// out = Hunk_Alloc ( (count + 1) * sizeof(*out));	
			edges = new medge_t[count + 1];
			
			loadmodel.edges = edges;
			loadmodel.numedges = count;
			
			ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			for (i = 0; i < count; i++)
			{
				edges[i] = new medge_t(bb);
			}
		}
		
		/*
		=================
		Mod_LoadTexinfo
		=================
		*/
		internal virtual void  Mod_LoadTexinfo(lump_t l)
		{
			texinfo_t in_Renamed;
			mtexinfo_t[] out_Renamed;
			mtexinfo_t step;
			int i, j, count;
			int next;
			System.String name;
			
			if ((l.filelen % texinfo_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
			
			count = l.filelen / texinfo_t.SIZE;
			// out = Hunk_Alloc ( count*sizeof(*out));
			out_Renamed = new mtexinfo_t[count];
			for (i = 0; i < count; i++)
			{
				out_Renamed[i] = new mtexinfo_t();
			}
			
			loadmodel.texinfo = out_Renamed;
			loadmodel.numtexinfo = count;
			
			ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			for (i = 0; i < count; i++)
			{
				
				in_Renamed = new texinfo_t(bb);
				out_Renamed[i].vecs = in_Renamed.vecs;
				out_Renamed[i].flags = in_Renamed.flags;
				next = in_Renamed.nexttexinfo;
				if (next > 0)
					out_Renamed[i].next = loadmodel.texinfo[next];
				else
					out_Renamed[i].next = null;
				
				name = "textures/" + in_Renamed.texture + ".wal";
				
				out_Renamed[i].image = GL_FindImage(name, it_wall);
				if (out_Renamed[i].image == null)
				{
					VID.Printf(Defines.PRINT_ALL, "Couldn't load " + name + '\n');
					out_Renamed[i].image = r_notexture;
				}
			}
			
			// count animation frames
			for (i = 0; i < count; i++)
			{
				out_Renamed[i].numframes = 1;
				for (step = out_Renamed[i].next; (step != null) && (step != out_Renamed[i]); step = step.next)
					out_Renamed[i].numframes++;
			}
		}
		
		/*
		================
		CalcSurfaceExtents
		
		Fills in s.texturemins[] and s.extents[]
		================
		*/
		internal virtual void  CalcSurfaceExtents(msurface_t s)
		{
			float[] mins = new float[]{0, 0};
			float[] maxs = new float[]{0, 0};
			float val;
			
			int j, e;
			mvertex_t v;
			int[] bmins = new int[]{0, 0};
			int[] bmaxs = new int[]{0, 0};
			
			mins[0] = mins[1] = 999999;
			maxs[0] = maxs[1] = - 99999;
			
			mtexinfo_t tex = s.texinfo;
			
			for (int i = 0; i < s.numedges; i++)
			{
				e = loadmodel.surfedges[s.firstedge + i];
				if (e >= 0)
					v = loadmodel.vertexes[loadmodel.edges[e].v[0]];
				else
					v = loadmodel.vertexes[loadmodel.edges[- e].v[1]];
				
				for (j = 0; j < 2; j++)
				{
					val = v.position[0] * tex.vecs[j][0] + v.position[1] * tex.vecs[j][1] + v.position[2] * tex.vecs[j][2] + tex.vecs[j][3];
					if (val < mins[j])
						mins[j] = val;
					if (val > maxs[j])
						maxs[j] = val;
				}
			}
			
			for (int i = 0; i < 2; i++)
			{
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				bmins[i] = (int) System.Math.Floor(mins[i] / 16);
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				bmaxs[i] = (int) System.Math.Ceiling(maxs[i] / 16);
				
				s.texturemins[i] = (short) (bmins[i] * 16);
				s.extents[i] = (short) ((bmaxs[i] - bmins[i]) * 16);
			}
		}
		
		/*
		=================
		Mod_LoadFaces
		=================
		*/
		internal virtual void  Mod_LoadFaces(lump_t l)
		{
			qfiles.dface_t in_Renamed;
			msurface_t[] out_Renamed;
			int i, count, surfnum;
			int planenum, side;
			int ti;
			
			if ((l.filelen % qfiles.dface_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
			
			count = l.filelen / qfiles.dface_t.SIZE;
			// out = Hunk_Alloc ( count*sizeof(*out));
			out_Renamed = new msurface_t[count];
			
			loadmodel.surfaces = out_Renamed;
			loadmodel.numsurfaces = count;
			
			ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			currentmodel = loadmodel;
			
			GL_BeginBuildingLightmaps(loadmodel);
			
			for (surfnum = 0; surfnum < count; surfnum++)
			{
				in_Renamed = new qfiles.dface_t(bb);
				out_Renamed[surfnum] = new msurface_t();
				out_Renamed[surfnum].firstedge = in_Renamed.firstedge;
				out_Renamed[surfnum].numedges = in_Renamed.numedges;
				out_Renamed[surfnum].flags = 0;
				out_Renamed[surfnum].polys = null;
				
				planenum = in_Renamed.planenum;
				side = in_Renamed.side;
				if (side != 0)
					out_Renamed[surfnum].flags |= Defines.SURF_PLANEBACK;
				
				out_Renamed[surfnum].plane = loadmodel.planes[planenum];
				
				ti = in_Renamed.texinfo;
				if (ti < 0 || ti >= loadmodel.numtexinfo)
					Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: bad texinfo number");
				
				out_Renamed[surfnum].texinfo = loadmodel.texinfo[ti];
				
				CalcSurfaceExtents(out_Renamed[surfnum]);
				
				// lighting info
				
				for (i = 0; i < Defines.MAXLIGHTMAPS; i++)
					out_Renamed[surfnum].styles[i] = in_Renamed.styles[i];
				
				i = in_Renamed.lightofs;
				if (i == - 1)
					out_Renamed[surfnum].samples = null;
				else
				{
					ByteBuffer pointer = ByteBuffer.wrap(loadmodel.lightdata);
					pointer.position(i);
					pointer = pointer.slice();
					pointer.mark();
					out_Renamed[surfnum].samples = pointer; // subarray
				}
				
				// set the drawing flags
				
				if ((out_Renamed[surfnum].texinfo.flags & Defines.SURF_WARP) != 0)
				{
					out_Renamed[surfnum].flags |= Defines.SURF_DRAWTURB;
					for (i = 0; i < 2; i++)
					{
						out_Renamed[surfnum].extents[i] = 16384;
						out_Renamed[surfnum].texturemins[i] = - 8192;
					}
					GL_SubdivideSurface(out_Renamed[surfnum]); // cut up polygon for warps
				}
				
				// create lightmaps and polygons
				if ((out_Renamed[surfnum].texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33 | Defines.SURF_TRANS66 | Defines.SURF_WARP)) == 0)
					GL_CreateSurfaceLightmap(out_Renamed[surfnum]);
				
				if ((out_Renamed[surfnum].texinfo.flags & Defines.SURF_WARP) == 0)
					GL_BuildPolygonFromSurface(out_Renamed[surfnum]);
			}
			GL_EndBuildingLightmaps();
		}
		
		
		/*
		=================
		Mod_SetParent
		=================
		*/
		internal virtual void  Mod_SetParent(mnode_t node, mnode_t parent)
		{
			node.parent = parent;
			if (node.contents != - 1)
				return ;
			Mod_SetParent(node.children[0], node);
			Mod_SetParent(node.children[1], node);
		}
		
		/*
		=================
		Mod_LoadNodes
		=================
		*/
		internal virtual void  Mod_LoadNodes(lump_t l)
		{
			int i, j, count, p;
			qfiles.dnode_t in_Renamed;
			mnode_t[] out_Renamed;
			
			if ((l.filelen % qfiles.dnode_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
			
			count = l.filelen / qfiles.dnode_t.SIZE;
			// out = Hunk_Alloc ( count*sizeof(*out));	
			out_Renamed = new mnode_t[count];
			
			loadmodel.nodes = out_Renamed;
			loadmodel.numnodes = count;
			
			ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			// initialize the tree array
			for (i = 0; i < count; i++)
				out_Renamed[i] = new mnode_t(); // do first before linking 
			
			// fill and link the nodes
			for (i = 0; i < count; i++)
			{
				in_Renamed = new qfiles.dnode_t(bb);
				for (j = 0; j < 3; j++)
				{
					out_Renamed[i].mins[j] = in_Renamed.mins[j];
					out_Renamed[i].maxs[j] = in_Renamed.maxs[j];
				}
				
				p = in_Renamed.planenum;
				out_Renamed[i].plane = loadmodel.planes[p];
				
				out_Renamed[i].firstsurface = in_Renamed.firstface;
				out_Renamed[i].numsurfaces = in_Renamed.numfaces;
				out_Renamed[i].contents = - 1; // differentiate from leafs
				
				for (j = 0; j < 2; j++)
				{
					p = in_Renamed.children[j];
					if (p >= 0)
						out_Renamed[i].children[j] = loadmodel.nodes[p];
					else
						out_Renamed[i].children[j] = loadmodel.leafs[- 1 - p]; // mleaf_t extends mnode_t
				}
			}
			
			Mod_SetParent(loadmodel.nodes[0], null); // sets nodes and leafs
		}
		
		/*
		=================
		Mod_LoadLeafs
		=================
		*/
		internal virtual void  Mod_LoadLeafs(lump_t l)
		{
			qfiles.dleaf_t in_Renamed;
			mleaf_t[] out_Renamed;
			int i, j, count, p;
			
			if ((l.filelen % qfiles.dleaf_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
			
			count = l.filelen / qfiles.dleaf_t.SIZE;
			// out = Hunk_Alloc ( count*sizeof(*out));
			out_Renamed = new mleaf_t[count];
			
			loadmodel.leafs = out_Renamed;
			loadmodel.numleafs = count;
			
			ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			for (i = 0; i < count; i++)
			{
				in_Renamed = new qfiles.dleaf_t(bb);
				out_Renamed[i] = new mleaf_t();
				for (j = 0; j < 3; j++)
				{
					out_Renamed[i].mins[j] = in_Renamed.mins[j];
					out_Renamed[i].maxs[j] = in_Renamed.maxs[j];
				}
				
				out_Renamed[i].contents = in_Renamed.contents;
				out_Renamed[i].cluster = in_Renamed.cluster;
				out_Renamed[i].area = in_Renamed.area;
				
				out_Renamed[i].setMarkSurface(in_Renamed.firstleafface, loadmodel.marksurfaces);
				out_Renamed[i].nummarksurfaces = in_Renamed.numleaffaces;
			}
		}
		
		
		/*
		=================
		Mod_LoadMarksurfaces
		=================
		*/
		internal virtual void  Mod_LoadMarksurfaces(lump_t l)
		{
			int i, j, count;
			
			msurface_t[] out_Renamed;
			
			if ((l.filelen % Defines.SIZE_OF_SHORT) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
			count = l.filelen / Defines.SIZE_OF_SHORT;
			// out = Hunk_Alloc ( count*sizeof(*out));	
			out_Renamed = new msurface_t[count];
			
			loadmodel.marksurfaces = out_Renamed;
			loadmodel.nummarksurfaces = count;
			
			ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			for (i = 0; i < count; i++)
			{
				j = bb.getShort();
				if (j < 0 || j >= loadmodel.numsurfaces)
					Com.Error(Defines.ERR_DROP, "Mod_ParseMarksurfaces: bad surface number");
				
				out_Renamed[i] = loadmodel.surfaces[j];
			}
		}
		
		
		/*
		=================
		Mod_LoadSurfedges
		=================
		*/
		internal virtual void  Mod_LoadSurfedges(lump_t l)
		{
			int i, count;
			int[] offsets;
			
			if ((l.filelen % Defines.SIZE_OF_INT) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
			
			count = l.filelen / Defines.SIZE_OF_INT;
			if (count < 1 || count >= Defines.MAX_MAP_SURFEDGES)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: bad surfedges count in " + loadmodel.name + ": " + count);
			
			offsets = new int[count];
			
			loadmodel.surfedges = offsets;
			loadmodel.numsurfedges = count;
			
			ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			for (i = 0; i < count; i++)
				offsets[i] = bb.getInt();
		}
		
		
		/*
		=================
		Mod_LoadPlanes
		=================
		*/
		internal virtual void  Mod_LoadPlanes(lump_t l)
		{
			int i, j;
			cplane_t[] out_Renamed;
			qfiles.dplane_t in_Renamed;
			int count;
			int bits;
			
			if ((l.filelen % qfiles.dplane_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
			
			count = l.filelen / qfiles.dplane_t.SIZE;
			// out = Hunk_Alloc ( count*2*sizeof(*out));
			out_Renamed = new cplane_t[count * 2];
			
			loadmodel.planes = out_Renamed;
			loadmodel.numplanes = count;
			
			ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			for (i = 0; i < count; i++)
			{
				bits = 0;
				in_Renamed = new qfiles.dplane_t(bb);
				out_Renamed[i] = new cplane_t();
				for (j = 0; j < 3; j++)
				{
					out_Renamed[i].normal[j] = in_Renamed.normal[j];
					if (out_Renamed[i].normal[j] < 0)
						bits |= (1 << j);
				}
				
				out_Renamed[i].dist = in_Renamed.dist;
				out_Renamed[i].type = (sbyte) in_Renamed.type;
				out_Renamed[i].signbits = (sbyte) bits;
			}
		}
		
		/*
		=================
		Mod_LoadBrushModel
		=================
		*/
		internal virtual void  Mod_LoadBrushModel(model_t mod, ByteBuffer buffer)
		{
			int i;
			qfiles.dheader_t header;
			mmodel_t bm;
			
			loadmodel.type = mod_brush;
			if (loadmodel != mod_known[0])
				Com.Error(Defines.ERR_DROP, "Loaded a brush model after the world");
			
			header = new qfiles.dheader_t(buffer);
			
			i = header.version;
			if (i != Defines.BSPVERSION)
				Com.Error(Defines.ERR_DROP, "Mod_LoadBrushModel: " + mod.name + " has wrong version number (" + i + " should be " + Defines.BSPVERSION + ")");
			
			mod_base = fileBuffer; //(byte *)header;
			
			// load into heap
			Mod_LoadVertexes(header.lumps[Defines.LUMP_VERTEXES]); // ok
			Mod_LoadEdges(header.lumps[Defines.LUMP_EDGES]); // ok
			Mod_LoadSurfedges(header.lumps[Defines.LUMP_SURFEDGES]); // ok
			Mod_LoadLighting(header.lumps[Defines.LUMP_LIGHTING]); // ok
			Mod_LoadPlanes(header.lumps[Defines.LUMP_PLANES]); // ok
			Mod_LoadTexinfo(header.lumps[Defines.LUMP_TEXINFO]); // ok
			Mod_LoadFaces(header.lumps[Defines.LUMP_FACES]); // ok
			Mod_LoadMarksurfaces(header.lumps[Defines.LUMP_LEAFFACES]);
			Mod_LoadVisibility(header.lumps[Defines.LUMP_VISIBILITY]); // ok
			Mod_LoadLeafs(header.lumps[Defines.LUMP_LEAFS]); // ok
			Mod_LoadNodes(header.lumps[Defines.LUMP_NODES]); // ok
			Mod_LoadSubmodels(header.lumps[Defines.LUMP_MODELS]);
			mod.numframes = 2; // regular and alternate animation
			
			//
			// set up the submodels
			//
			model_t starmod;
			
			for (i = 0; i < mod.numsubmodels; i++)
			{
				
				bm = mod.submodels[i];
				starmod = mod_inline[i] = loadmodel.copy();
				
				starmod.firstmodelsurface = bm.firstface;
				starmod.nummodelsurfaces = bm.numfaces;
				starmod.firstnode = bm.headnode;
				if (starmod.firstnode >= loadmodel.numnodes)
					Com.Error(Defines.ERR_DROP, "Inline model " + i + " has bad firstnode");
				
				Math3D.VectorCopy(bm.maxs, starmod.maxs);
				Math3D.VectorCopy(bm.mins, starmod.mins);
				starmod.radius = bm.radius;
				
				if (i == 0)
					loadmodel = starmod.copy();
				
				starmod.numleafs = bm.visleafs;
			}
		}
		
		/*
		==============================================================================
		
		ALIAS MODELS
		
		==============================================================================
		*/
		
		/*
		=================
		Mod_LoadAliasModel
		=================
		*/
		internal virtual void  Mod_LoadAliasModel(model_t mod, ByteBuffer buffer)
		{
			int i, j;
			qfiles.dmdl_t pheader;
			qfiles.dstvert_t[] poutst;
			qfiles.dtriangle_t[] pouttri;
			qfiles.daliasframe_t[] poutframe;
			int[] poutcmd;
			
			pheader = new qfiles.dmdl_t(buffer);
			
			if (pheader.version != qfiles.ALIAS_VERSION)
				Com.Error(Defines.ERR_DROP, "%s has wrong version number (%i should be %i)", new Vargs(3).add(mod.name).add(pheader.version).add(qfiles.ALIAS_VERSION));
			
			if (pheader.skinheight > MAX_LBM_HEIGHT)
				Com.Error(Defines.ERR_DROP, "model " + mod.name + " has a skin taller than " + MAX_LBM_HEIGHT);
			
			if (pheader.num_xyz <= 0)
				Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no vertices");
			
			if (pheader.num_xyz > qfiles.MAX_VERTS)
				Com.Error(Defines.ERR_DROP, "model " + mod.name + " has too many vertices");
			
			if (pheader.num_st <= 0)
				Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no st vertices");
			
			if (pheader.num_tris <= 0)
				Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no triangles");
			
			if (pheader.num_frames <= 0)
				Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no frames");
			
			//
			// load base s and t vertices (not used in gl version)
			//
			poutst = new qfiles.dstvert_t[pheader.num_st];
			buffer.position(pheader.ofs_st);
			for (i = 0; i < pheader.num_st; i++)
			{
				poutst[i] = new qfiles.dstvert_t(buffer);
			}
			
			//
			//	   load triangle lists
			//
			pouttri = new qfiles.dtriangle_t[pheader.num_tris];
			buffer.position(pheader.ofs_tris);
			for (i = 0; i < pheader.num_tris; i++)
			{
				pouttri[i] = new qfiles.dtriangle_t(buffer);
			}
			
			//
			//	   load the frames
			//
			poutframe = new qfiles.daliasframe_t[pheader.num_frames];
			buffer.position(pheader.ofs_frames);
			for (i = 0; i < pheader.num_frames; i++)
			{
				poutframe[i] = new qfiles.daliasframe_t(buffer);
				// verts are all 8 bit, so no swapping needed
				poutframe[i].verts = new int[pheader.num_xyz];
				for (int k = 0; k < pheader.num_xyz; k++)
				{
					poutframe[i].verts[k] = buffer.getInt();
				}
			}
			
			mod.type = mod_alias;
			
			//
			// load the glcmds
			//
			poutcmd = new int[pheader.num_glcmds];
			buffer.position(pheader.ofs_glcmds);
			for (i = 0; i < pheader.num_glcmds; i++)
				poutcmd[i] = buffer.getInt(); // LittleLong (pincmd[i]);
			
			// register all skins
			System.String[] skinNames = new System.String[pheader.num_skins];
			sbyte[] nameBuf = new sbyte[qfiles.MAX_SKINNAME];
			buffer.position(pheader.ofs_skins);
			for (i = 0; i < pheader.num_skins; i++)
			{
				buffer.get_Renamed(nameBuf);
				skinNames[i] = new System.String(SupportClass.ToCharArray(SupportClass.ToByteArray(nameBuf))).Trim();
				mod.skins[i] = GL_FindImage(skinNames[i], it_skin);
			}
			
			// set the model arrays
			pheader.skinNames = skinNames; // skin names
			pheader.stVerts = poutst; // textur koordinaten
			pheader.triAngles = pouttri; // dreiecke
			pheader.glCmds = poutcmd; // STRIP or FAN
			pheader.aliasFrames = poutframe; // frames mit vertex array
			
			mod.extradata = pheader;
			
			mod.mins[0] = - 32;
			mod.mins[1] = - 32;
			mod.mins[2] = - 32;
			mod.maxs[0] = 32;
			mod.maxs[1] = 32;
			mod.maxs[2] = 32;
		}
		
		/*
		==============================================================================
		
		SPRITE MODELS
		
		==============================================================================
		*/
		
		/*
		=================
		Mod_LoadSpriteModel
		=================
		*/
		internal virtual void  Mod_LoadSpriteModel(model_t mod, ByteBuffer buffer)
		{
			qfiles.dsprite_t sprout = new qfiles.dsprite_t(buffer);
			
			if (sprout.version != qfiles.SPRITE_VERSION)
				Com.Error(Defines.ERR_DROP, "%s has wrong version number (%i should be %i)", new Vargs(3).add(mod.name).add(sprout.version).add(qfiles.SPRITE_VERSION));
			
			if (sprout.numframes > qfiles.MAX_MD2SKINS)
				Com.Error(Defines.ERR_DROP, "%s has too many frames (%i > %i)", new Vargs(3).add(mod.name).add(sprout.numframes).add(qfiles.MAX_MD2SKINS));
			
			for (int i = 0; i < sprout.numframes; i++)
			{
				mod.skins[i] = GL_FindImage(sprout.frames[i].name, it_sprite);
			}
			
			mod.type = mod_sprite;
			mod.extradata = sprout;
		}
		
		//	  =============================================================================
		
		/*
		@@@@@@@@@@@@@@@@@@@@@
		R_BeginRegistration
		
		Specifies the model that will be used as the world
		@@@@@@@@@@@@@@@@@@@@@
		*/
		protected internal virtual void  R_BeginRegistration(System.String model)
		{
			cvar_t flushmap;
			
			Polygon.reset();
			
			registration_sequence++;
			r_oldviewcluster = - 1; // force markleafs
			
			System.String fullname = "maps/" + model + ".bsp";
			
			// explicitly free the old map if different
			// this guarantees that mod_known[0] is the world map
			flushmap = Cvar.Get("flushmap", "0", 0);
			if (!mod_known[0].name.Equals(fullname) || flushmap.value_Renamed != 0.0f)
				Mod_Free(mod_known[0]);
			r_worldmodel = Mod_ForName(fullname, true);
			
			r_viewcluster = - 1;
		}
		
		
		/*
		@@@@@@@@@@@@@@@@@@@@@
		R_RegisterModel
		
		@@@@@@@@@@@@@@@@@@@@@
		*/
		protected internal virtual model_t R_RegisterModel(System.String name)
		{
			model_t mod = null;
			int i;
			qfiles.dsprite_t sprout;
			qfiles.dmdl_t pheader;
			
			mod = Mod_ForName(name, false);
			if (mod != null)
			{
				mod.registration_sequence = registration_sequence;
				
				// register any images used by the models
				if (mod.type == mod_sprite)
				{
					sprout = (qfiles.dsprite_t) mod.extradata;
					for (i = 0; i < sprout.numframes; i++)
						mod.skins[i] = GL_FindImage(sprout.frames[i].name, it_sprite);
				}
				else if (mod.type == mod_alias)
				{
					pheader = (qfiles.dmdl_t) mod.extradata;
					for (i = 0; i < pheader.num_skins; i++)
						mod.skins[i] = GL_FindImage(pheader.skinNames[i], it_skin);
					// PGM
					mod.numframes = pheader.num_frames;
					// PGM
				}
				else if (mod.type == mod_brush)
				{
					for (i = 0; i < mod.numtexinfo; i++)
						mod.texinfo[i].image.registration_sequence = registration_sequence;
				}
			}
			return mod;
		}
		
		
		/*
		@@@@@@@@@@@@@@@@@@@@@
		R_EndRegistration
		
		@@@@@@@@@@@@@@@@@@@@@
		*/
		protected internal virtual void  R_EndRegistration()
		{
			model_t mod;
			
			for (int i = 0; i < mod_numknown; i++)
			{
				mod = mod_known[i];
				if (mod.name.Length == 0)
					continue;
				if (mod.registration_sequence != registration_sequence)
				{
					// don't need this model
					Mod_Free(mod);
				}
			}
			GL_FreeUnusedImages();
		}
		
		
		//	  =============================================================================
		
		
		/*
		================
		Mod_Free
		================
		*/
		internal virtual void  Mod_Free(model_t mod)
		{
			mod.clear();
		}
		
		/*
		================
		Mod_FreeAll
		================
		*/
		internal override void  Mod_FreeAll()
		{
			for (int i = 0; i < mod_numknown; i++)
			{
				if (mod_known[i].extradata != null)
					Mod_Free(mod_known[i]);
			}
		}
	}
}